%include "sysconfig.inc"
;=================================================
;导出
;-------------------------------------------------
global _Low_RestoreTask

;异常处理函数入口
global Handle_Trap_DE0       ;  0: Divide-by-zero */
global Handle_Trap_DB        ;  1: Debug */
global Handle_Trap_NMI       ;  2: Non-maskable Interrupt */
global Handle_Trap_BP        ;  3: Breakpoint */
global Handle_Trap_OF        ;  4: Overflow */
global Handle_Trap_BR        ;  5: Bound Range Exceeded */
global Handle_Trap_UD        ;  6: Invalid Opcode */
global Handle_Trap_NM        ;  7: Device Not Available */
global Handle_Trap_DF        ;  8: Double Fault */
global Handle_Trap_OLD_MF    ;  9: Coprocessor Segment Overrun */
global Handle_Trap_TS        ; 10: Invalid TSS */
global Handle_Trap_NP        ; 11: Segment Not Present */
global Handle_Trap_SS        ; 12: Stack Segment Fault */
global Handle_Trap_GP        ; 13: General Protection Fault */
global Handle_Trap_PF        ; 14: Page Fault */
global Handle_Trap_SPURIOUS  ; 15: Spurious Interrupt */
global Handle_Trap_MF        ; 16: x87 Floating-Point Exception */
global Handle_Trap_AC        ; 17: Alignment Check */
global Handle_Trap_MC        ; 18: Machine Check */
global Handle_Trap_XF        ; 19: SIMD Floating-Point Exception */
global Handle_Trap_IRET      ; 32: IRET Exception */

;硬件中断处理函数入口
global hwint_00
global hwint_01
global hwint_02
global hwint_03
global hwint_04
global hwint_05
global hwint_06
global hwint_07
global hwint_08
global hwint_09
global hwint_10
global hwint_11
global hwint_12
global hwint_13
global hwint_14
global hwint_15
;=================================================
;导入
;-------------------------------------------------
extern ExceptionHandler
extern system_dump
extern irq_handler_table
extern current
extern tss
extern _preempt
;=================================================
;宏定义
;-------------------------------------------------
INT_M_CTL           equ         0x20
INT_M_CTLMSK        equ         0x21
INT_S_CTL           equ         0xA0
INT_S_CTLMSK        equ         0xA1
EOI                 equ         0x20
DISABLE             equ         0

;进程PCB相关
GS_OFFSET           equ         0
FS_OFFSET           equ         GS_OFFSET + 4
ES_OFFSET           equ         FS_OFFSET + 4
DS_OFFSET           equ         ES_OFFSET + 4

LINK_OFFSET         equ         48
EIP_OFFSET          equ         LINK_OFFSET + 4
CS_OFFSET           equ         EIP_OFFSET + 4
EFLAGS_OFFSET       equ         CS_OFFSET + 4
ESP_OFFSET          equ         EFLAGS_OFFSET + 4
SS_OFFSET           equ         ESP_OFFSET + 4

SEL_LDT_OFFSET      equ         SS_OFFSET + 4
LDT_OFFSET          equ         SEL_LDT_OFFSET + 4

;ss0 在TSS中的偏移
TSS_ESP0_OFFSET     equ         4
;=================================================
; 主 8059A
;-------------------------------------------------
%macro hwint_master 1
    call _Low_SaveTask                          ;保存上文

    in al, INT_M_CTLMSK
    or al, (1 << %1)
    out INT_M_CTLMSK, al

    mov al, EOI
    out INT_M_CTL, al
    nop
    sti                                         ;打开中断

    push %1
    call [irq_handler_table + 4 * %1]
    add esp, 4

    cli
    cmp eax, DISABLE
    je .DISABLE

    in al, INT_M_CTLMSK
    and al, ~(1 << %1)
    out INT_M_CTLMSK, al

.DISABLE:
    ret
%endmacro
;-------------------------------------------------
hwint_00:
    hwint_master 0
hwint_01:
    hwint_master 1
hwint_02:
    hwint_master 2
hwint_03:
    hwint_master 3
hwint_04:
    hwint_master 4
hwint_05:
    hwint_master 5
hwint_06:
    hwint_master 6
hwint_07:
    hwint_master 7
;=================================================
; 从 8059A
;-------------------------------------------------
%macro hwint_slave 1

    call _Low_SaveTask                          ;保存上文

    in al, INT_S_CTLMSK
    or al, (1 << %1 - 8)
    out INT_S_CTLMSK, al

    mov al, EOI
    out INT_M_CTL, al
    nop
    out INT_S_CTL, al
    sti                                         ;打开中断

    push %1
    call [irq_handler_table + 4 * %1]
    add esp, 4

    cli
    cmp eax, DISABLE
    je .DISABLE

    in al, INT_S_CTLMSK
    and al, ~(1 << %1 - 8)
    out INT_S_CTLMSK, al

.DISABLE:
    ret
%endmacro
;-------------------------------------------------
hwint_08:
    hwint_slave 8
hwint_09:
    hwint_slave 9
hwint_10:
    hwint_slave 10
hwint_11:
    hwint_slave 11
hwint_12:
    hwint_slave 12
hwint_13:
    hwint_slave 13
hwint_14:
    hwint_slave 14
hwint_15:
    hwint_slave 15
;=================================================
; 异常
;-------------------------------------------------
Handle_Trap_DE0:       ;  0: Divide-by-zero */
    call _Low_SaveTask
    push 0xffffffff
    jmp EXCEP_HANDLE
Handle_Trap_DB:        ;  1: Debug */
    call _Low_SaveTask
    push 0xffffffff
    push 0x01
    jmp EXCEP_HANDLE
Handle_Trap_NMI:       ;  2: Non-maskable Interrupt */
    call _Low_SaveTask
    push 0xffffffff
    push 0x02
    jmp EXCEP_HANDLE
Handle_Trap_BP:        ;  3: Breakpoint */
    call _Low_SaveTask
    push 0xffffffff
    push 0x03
    jmp EXCEP_HANDLE
Handle_Trap_OF:        ;  4: Overflow */
    call _Low_SaveTask
    push 0xffffffff
    push 0x04
    jmp EXCEP_HANDLE
Handle_Trap_BR:        ;  5: Bound Range Exceeded */
    call _Low_SaveTask
    push 0xffffffff
    push 0x05
    jmp EXCEP_HANDLE
Handle_Trap_UD:        ;  6: Invalid Opcode */
    ;call _Low_SaveTask
    push 0xffffffff
    push 0x06
    jmp EXCEP_HANDLE
Handle_Trap_NM:        ;  7: Device Not Available */
    call _Low_SaveTask
    push 0xffffffff
    push 0x07
    jmp EXCEP_HANDLE
Handle_Trap_DF:        ;  8: Double Fault */
    call _Low_SaveTask
    push 0xffffffff
    push 0x08
    jmp EXCEP_HANDLE
Handle_Trap_OLD_MF:    ;  9: Coprocessor Segment Overrun */
    call _Low_SaveTask
    push 0xffffffff
    push 0x09
    jmp EXCEP_HANDLE
Handle_Trap_TS:        ; 10: Invalid TSS */
    call _Low_SaveTask
    push 0x0a
    jmp EXCEP_HANDLE
Handle_Trap_NP:        ; 11: Segment Not Present */
    call _Low_SaveTask
    push 0x0b
    jmp EXCEP_HANDLE
Handle_Trap_SS:        ; 12: Stack Segment Fault */
    call _Low_SaveTask
    push 0x0c
    jmp EXCEP_HANDLE
Handle_Trap_GP:        ; 13: General Protection Fault */
    call _Low_SaveTask
    push 0x0d
    jmp EXCEP_HANDLE
Handle_Trap_PF:        ; 14: Page Fault */
    call _Low_SaveTask
    push 0x0e
    jmp EXCEP_HANDLE
Handle_Trap_SPURIOUS:  ; 15: Spurious Interrupt */
    call _Low_SaveTask
    push 0xffffffff
    push 0x0f
    jmp EXCEP_HANDLE
Handle_Trap_MF:        ; 16: x87 Floating-Point Exception */
    call _Low_SaveTask
    push 0xffffffff
    push 0x10
    jmp EXCEP_HANDLE
Handle_Trap_AC:        ; 17: Alignment Check */
    call _Low_SaveTask
    push 0xffffffff
    push 0x11
    jmp EXCEP_HANDLE
Handle_Trap_MC:        ; 18: Machine Check */
    call _Low_SaveTask
    push 0xffffffff
    push 0x12
    jmp EXCEP_HANDLE
Handle_Trap_XF:        ; 19: SIMD Floating-Point Exception */
    call _Low_SaveTask
    push 0xffffffff
    push 0x13
    jmp EXCEP_HANDLE
Handle_Trap_IRET:      ; 32: IRET Exception */
    call _Low_SaveTask
    push 0xffffffff
    push 0x100
    jmp EXCEP_HANDLE

EXCEP_HANDLE:
    call ExceptionHandler
    add esp, 4 * 2
.hlt:
    jmp system_dump
;=================================================
; 保存现场  esp 指向进程的 thread_info 的顶
;-------------------------------------------------
_Low_SaveTask:
    pushad
    push ds
    push es
    push fs
    push gs

    ;开始恢复内核数据段
    mov ax, ss
    mov ds, ax
    mov es, ax
    mov esi, esp                                ;此时esi指向进程thread_info

    ;判断是否需要切换内核堆栈
    inc byte [_preempt]
    cmp byte [_preempt], 0
    jne ._preempt

    mov esp, KERNEL_STACK_BASE
    push _Low_RestoreTask
    jmp [esi + LINK_OFFSET]

._preempt:
    push RESTORE_FROM_INT
    jmp [esi + LINK_OFFSET]
;=================================================
; 恢复现场函数
;-------------------------------------------------
extern ldt_sel

_Low_RestoreTask:
    mov esp, [current]
    lldt [ldt_sel]                     ;加载进程的 ldt

    lea eax, [esp + SEL_LDT_OFFSET]
    mov dword [tss + TSS_ESP0_OFFSET], eax          ;中断会自动切换到tss.esp0 ，这里设置栈指针为进程 thread_info 顶部
RESTORE_FROM_INT:
    dec byte [_preempt]

    pop gs
    pop fs
    pop es
    pop ds
    popad

    add esp, 4                                      ;跳过 LINK_REG：本质是 call 自动压入的返回地址
    iretd                                           ;中断返回

